package com.ifnk.portaljava.dapWeb.socket;
/**
 * <p>Title: 读取proxy发送的数据类</p>
 *
 * <p>Description: </p>
 *
 * <p>Copyright: Copyright (c) 2010</p>
 *
 * <p>Company: </p>
 *
 * @author WenSheng.Song.hlyt@gmail.com
 * @version 1.0
 */

import com.alibaba.fastjson.JSON;
import com.ifnk.portaljava.controller.MsgWebsocketController;
import com.ifnk.portaljava.controller.PacketController;
import com.ifnk.portaljava.dapWeb.common.Constants;
import com.ifnk.portaljava.dapWeb.dapweb.PacketHeader;
import com.ifnk.portaljava.dapWeb.dapweb.SignallingPacket;
import com.ifnk.portaljava.dapWeb.dataPacket.TypeDefine;
import com.ifnk.portaljava.entity.PcapResult;
import com.ifnk.portaljava.entity.ResultCode;
import com.ifnk.portaljava.entity.ServiceResult;
import com.ifnk.portaljava.entity.SuccessResult;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;

import static com.ifnk.portaljava.dapWeb.hlyt.servlet.CallTraceServlet.CDRKEY;
import static java.lang.Thread.sleep;

public class SocketRead {

    private SocketProperty sp;
    private ClientProperty cp;
    private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);


    public static String bytesToHex(byte[] bytes) {
        byte[] hexChars = new byte[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = HEX_ARRAY[v >>> 4];
            hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
        }
        return new String(hexChars, StandardCharsets.UTF_8);
    }

    private static final int BUFF_SIZE = 1 * 1024 * 1024; // 1M size of buf

    private byte[] data = new byte[BUFF_SIZE];
    private int dLength = 0; // 数据长度
    private int nEnd = 0; // 偏移标
    private int nStart = 0; // 检索开始位
    private int count = 0; //接收包记数
    private int rawcount = 0; //接收包记数
    private int countLength = 0; //接收包的总容量
    private static Log loger = LogFactory.getLog(SocketRead.class);

    public SocketRead(SocketProperty sp, ClientProperty cp) {
        this.sp = sp;
        this.cp = cp;
        sp.setFlag(true);
    }

    public void recieveDataFromPROXY(String clientId) {
        System.out.println("从proxy接数据");
        BufferedInputStream bis = new BufferedInputStream(sp.in);
        while (sp.getFlag()) {
            try {
                String requestDateFromProxyId = cp.getRequestDateFromProxyId();
                loger.debug("hi, before socket read @" + requestDateFromProxyId);
                int rc = bis.read(data, nEnd, BUFF_SIZE - nEnd);
                //loger.debug("rc="+rc);
                if (rc < 0) {
                    sp.setFlag(true);
                    loger.error("WebServer arrive InputStream end and  not any more Data rc == -1");
                    ResultCode resultCode = ResultCode.builder().code(500).message("rc == -1 可能proxy服务器网络连不上!").build();
                    ServiceResult<Object> rst = ServiceResult.builder().msg(resultCode.getMessage()).code(resultCode.getCode()).success(false).build();
                    MsgWebsocketController.sendInfo(JSON.toJSONString(rst), clientId);
                    break;
                }
                if (rc == 0) {
                    //loger.debug("hi, before socket sleep @" + cp.getRequestDateFromProxyId());
                    sleep(100);
                    continue;
                }
                nEnd = nEnd + rc;
                dLength = dLength + rc;

                // this.saveDataToFile(data);
                if (FindSync()) {
                    while (sp.getFlag()) {
                        int p_len = checkPacket();
                        System.out.println("p_len = " + p_len);
                        if (p_len > 0) {
                            byte[] packet = new byte[p_len];
                            System.arraycopy(data, nStart, packet, 0, p_len);
                            System.out.println("packet = " + Arrays.toString(packet));
                            String hex = bytesToHex(packet);
                            System.out.println("hex = " + hex);
                            // this.saveDataToFile(packet);
                            nStart = nStart + p_len;
                            dLength = dLength - p_len;
                            countLength = countLength + p_len;


                            // 处理数据
                            int CMD = ((packet[4] & 0xf3) << 8) | packet[5];
                            //loger.debug("CMD="+CMD);
                            switch (CMD) {
                                case TypeDefine.WWW_CQ_START:
                                    count = 0;
                                    cp.packets.offer(packet);
                                    break;
                                case 257:
                                    break;
                                case 9008:
                                    doWWW_KEEPALIVE(packet);
                                    break;
                                case TypeDefine.WWW_CQ_RAWDATA:
                                    if (cp.isPktStoped()) break;
                                    cp.getRawDataPackets().offer(packet);
                                    // 启动处理rawData线程
                                    startProcessRawDataThread(1);
                                    break;
                                case TypeDefine.WWW_RQ_RAWDATA: //queryquery
                                    System.out.println("tcp 接受包");
                                    rawcount++;
                                    if (cp.isPktStoped()) break;
                                    cp.getRawDataPackets().offer(packet);
                                    // 启动处理rawData线程(分批次发)
                                    startProcessRawDataAllThread(1, clientId);
                                    break;
                                case TypeDefine.WWW_RQ_RAW_QUERY_COMPLETE: // queryquery complete
                                    Locale locale = Locale.ENGLISH;
//                                    PacketController.pktByteMap
                                    sleep(100);
                                    System.out.println("PacketController.pktByteMap = " + PacketController.pktByteMap);
                                    List<byte[]> list = PacketController.pktByteMap.get(clientId);
                                    // 先给数组去个重
                                    for (int i = 0; i < list.size(); i++) {
                                        for (int j = 0; j < list.size(); j++) {
                                            if (Arrays.equals(list.get(i), list.get(j))) {
                                                list.remove(j);
                                                j--;
                                            }
                                        }
                                    }
                                    // 根据时间进行排序
                                    list.sort((x, y) -> {
                                        PacketHeader headerX = new PacketHeader();
                                        headerX.DecodeHeader(x);
                                        int tsX = headerX.getTs_s();
                                        PacketHeader headerY = new PacketHeader();
                                        headerY.DecodeHeader(y);
                                        int tsY = headerY.getTs_s();
                                        return tsX - tsY;
                                    });
                                    SignallingPacket signallingPacket = new SignallingPacket();

                                    ResourceBundle localResource = ResourceBundle.getBundle("Socket", locale);
                                    String pcapfilepath = localResource.getString("pacpfilepath");
                                    String tshark = localResource.getString("tshark");
                                    String requestDateFromProxyId1 = cp.getRequestDateFromProxyId();
                                    String pcaproot = Paths.get(pcapfilepath, "summary", requestDateFromProxyId1, CDRKEY).toString();
                                    String fileNamePcap = Paths.get(pcaproot, CDRKEY + ".pcap").toString();
                                    System.out.println("fileNamePcap = " + fileNamePcap);

                                    ProcessRawDataAll processRawDataAll = new ProcessRawDataAll(cp, clientId);
                                    for (int i = 0; i < list.size(); i++) {
                                        byte[] pktBytes;
                                        if (i == 0) {
                                            // 第一次是 生成文件
                                            pktBytes = signallingPacket.convertPacketToPcap(list.get(i));
                                            processRawDataAll.savePacketToNativeFile(fileNamePcap, pcaproot, pktBytes, false);
                                        } else {
                                            // 后面是附加到pcap 文件 后面
                                            pktBytes = signallingPacket.TransformToDataPacket(list.get(i));
                                            processRawDataAll.savePacketToNativeFile(fileNamePcap, pcaproot, pktBytes, true);
                                        }
                                    }

                                    // 存完把 对应key 的数据删除
                                    PacketController.pktByteMap.remove(clientId);

                                    if (new File(fileNamePcap).exists()) {
                                        String tsharkArgu = cp.cdrPktInfoMap.get(CDRKEY).getTsharkArgu() == null ? "" : cp.cdrPktInfoMap.get(CDRKEY).getTsharkArgu();
                                        String tsharkChinese = cp.cdrPktInfoMap.get(CDRKEY).getTsharkChineseArgu();

                                        String detailCmd = tshark + " -r " + fileNamePcap + " " + tsharkArgu + " " + tsharkChinese + " " + " -T pdml | fxparser";
                                        String summaryCmd = tshark + " -r " + fileNamePcap + " " + tsharkArgu + " " + tsharkChinese + " " + "-T tabs";
                                        String hexCmd = tshark + " -r " + fileNamePcap + " " + tsharkArgu + " " + tsharkChinese + " " + "-T jsonraw";


                                        System.out.println("hexCmd = " + hexCmd);
                                        System.out.println("detailCmd = " + detailCmd);
                                        System.out.println("summaryCmd = " + summaryCmd);


                                        try {
                                            ProcessBuilder processBuilder = new ProcessBuilder();
                                            processBuilder.command("bash", "-c", detailCmd);
                                            Process process = processBuilder.start();
                                            String detailRes = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8);

                                            // String detailRes = IOUtils.toString(Runtime.getRuntime().exec(detailCmd).getInputStream(), StandardCharsets.UTF_8);
                                            String summaryRes = IOUtils.toString(Runtime.getRuntime().exec(summaryCmd).getInputStream(), StandardCharsets.UTF_8);
                                            String hexRes = IOUtils.toString(Runtime.getRuntime().exec(hexCmd).getInputStream(), StandardCharsets.UTF_8);

                                            MsgWebsocketController.sendInfo(JSON.toJSONString(new SuccessResult<>(new PcapResult(detailRes, summaryRes, hexRes))), clientId);
                                        } catch (IOException e) {

                                            ResultCode resultCode = ResultCode.builder().code(500).message(e.getMessage()).build();
                                            ServiceResult<Object> rst = ServiceResult.builder().msg(resultCode.getMessage()).code(resultCode.getCode()).success(false).build();
                                            MsgWebsocketController.sendInfo(JSON.toJSONString(rst), clientId);
                                        }
                                    } else {
                                        ResultCode resultCode = ResultCode.builder().code(500).message("查询未找到结果!").build();
                                        ServiceResult<Object> rst = ServiceResult.builder().msg(resultCode.getMessage()).code(resultCode.getCode()).success(false).build();
                                        MsgWebsocketController.sendInfo(JSON.toJSONString(rst), clientId);
                                        throw new RuntimeException("查询未找到结果!");
                                    }


                                    //startProcessRawDataComplete();
                                    sendProcessRawDataCompleteRightNow();
                                    break;
                                case TypeDefine.WWW_CT_RAWDATA:
                                    if (cp.isPktStoped()) break;
                                    cp.getRawDataPackets().

                                            offer(packet);
                                    // 启动处理rawData线程
//                                    startProcessRawDataThread(1);
                                    break;
                                case TypeDefine.WWW_CT_CDRDATA:
                                    if (cp.isStoped()) break;
                                    cp.packets.offer(packet);

                                    startPacketprocessThread(1);
                                    break;
                                case TypeDefine.WWW_CQ_CDRDATA:
                                    if (cp.isStoped()) break;
                                    count++;
                                    cp.setReceiveCdrCount(count);
                                    //loger.debug("count = "+count);
//								count++;
//								cp.setReceiveCdrCount(count);
//								cp.packets.offer(packet);
//								startPacketprocessThread(3);
                                    new

                                            PacketProcess(cp, sp).

                                            processCdr(packet);
                                    break;
                                default:
                                    System.out.println("默认");
                                    cp.packets.offer(packet);

                                    startPacketprocessThread(1);
                                    break;
                            }
                        } else {
                            if (dLength > 0)
                                System.arraycopy(data, nStart, data, 0, dLength);
                            nStart = 0;
                            nEnd = dLength;
                            break;
                        }
                    }
                }

            } catch (Exception e) {
                sp.setFlag(false);
                e.printStackTrace();
                loger.error("WebServer Exception " + e.toString());
                sp.destroySocket();
                break;
            }
        }
    }

    /*
     * 检查包的边界 找到返回true/未找到返回false
     */
    private boolean FindSync() {
        boolean synced = false;
        int i = 0;
        for (i = nStart; i < (nEnd - 1); i++) {
            if (data[i] != 0x7e)
                continue;
            if (data[i + 1] != 0x5a) {
                continue;
            }
            synced = true;
            break;
        }
        dLength = dLength - (i - nStart);
        nStart = i;
        return synced;
    }

    // 验证是否是包头的长度
    private int checkPacket() {
        if (dLength >= Constants.PACKETHEADER_LEN) {
            // 包的长度
            int packet_len = getPacketLen(data[nStart + 2], data[nStart + 3],
                    data[nStart + 7]);
            //loger.debug("packet_len:="+packet_len);
            if (dLength >= packet_len) {
                return packet_len;
            }
        }
        return 0;
    }

    // 获取包的长度
    private int getPacketLen(byte b1, byte b2, byte sc) {
        return ((b1 << 8) + (b2 & 0xFF)) * 4 - (sc & 0x3);
    }


    private void doWWW_KEEPALIVE(byte[] packet) {
        cp.setKeepLiveTime(System.currentTimeMillis() / 1000);
        cp.setKeepLive(true);
        sp.flag = true;
    }

    private void startProcessRawDataThread(int threadNumber) {
        if (cp.doRawDataThreads.size() < threadNumber) {
            for (int i = 0; i < threadNumber - cp.doRawDataThreads.size(); i++) {
                ProcessRawData doRawData = new ProcessRawData(cp);
                cp.addProcessRawData(doRawData);
                doRawData.start();
            }
        }
    }

    private void startProcessRawDataAllThread(int threadNumber, String clientId) {
        if (cp.doRawDataThreads.size() < threadNumber) {
            for (int i = 0; i < threadNumber - cp.doRawDataThreads.size(); i++) {
                ProcessRawData doRawData = new ProcessRawDataAll(cp, clientId);
                cp.addProcessRawData(doRawData);
                cp.processRawData = doRawData;
                doRawData.start();
            }
        }
    }

    private void sendProcessRawDataCompleteRightNow() {
        System.out.println("hi, got end of data @" + cp.getRequestDateFromProxyId());
        if (rawcount == 0) {
//            FlexUtil.sendKeepAliveMessage("no data", false, false, cp.getClientId());
            System.out.println("no data" + cp.getClientId());
        } else {
            System.out.println("hi, send end data to " + cp.getClientId());
//            FlexUtil.sendKeepAliveMessage("end data", false, false, cp.getClientId());
        }
        try {
            System.out.println("hi, sleep 30 second before next read from tcp");
            sleep(30000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void startProcessRawDataComplete() {

        new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        loger.debug("hi, got end of data @" + cp.getRequestDateFromProxyId());
                        if (rawcount == 0) {
                            FlexUtil.sendKeepAliveMessage("no data", false, false, cp.getClientId());
                        } else {
                            loger.debug("hi, send end data to" + cp.getClientId());
                            FlexUtil.sendKeepAliveMessage("end data", false, false, cp.getClientId());
                        }
                    }
                }
        ).start();
    }

    private void startPacketprocessThread(int threadNumber) {
        if ((cp.process.size() < threadNumber)
                && !cp.packets.isEmpty()) {
            for (int i = 0; i < (threadNumber - cp.process.size()); i++) {
                PacketProcess process = new PacketProcess(
                        cp, sp);
                cp.process.add(process);
                process.start();
            }
        }
    }

    public void saveDataToFile(byte[] data) throws Exception {
        DataOutputStream out = new DataOutputStream(new FileOutputStream(
                new File("C:\\Users\\s\\Desktop\\Desktopclient.dat"), true));
        out.write(data);
        out.flush();
        out.close();
    }


}
